在前面幾個章節中,應該或多或少都有看到函式在 TypeScript 中的寫法,它比 JavaScript 多了參數和返回值的型別設定,我們看一下函式基本的宣告方式:
宣告函式有下面種方式,如果函式沒有返回值的話,我們可以使用 void
型別,在 TypeScript 中的寫法為:
function greet(name: string): void {
console.log(`哈囉,${name}!`); // 輸出: 哈囉,威爾豬!
}
greet("威爾豬");
const greet = (name: string): void => {
console.log(`哈囉,${name}!`); // 輸出: 哈囉,威爾豬!
};
greet("威爾豬");
const greet = function (name: string): void {
console.log(`哈囉,${name}!`); // 輸出: 哈囉,威爾豬!
};
greet("威爾豬");
(function (): void {
let name: string = "威爾豬";
console.log(`哈囉,${name}!`); // 輸出: 哈囉,威爾豬!
})();
至於要用哪一種寫法,就看會不會使用到 this
,而 this 的指向在不同的情況下可能會有所不同,這取決於函式是如何被調用,相信被 JavaScript 荼毒過的人,對於 this 指向應該是一段痛苦的回憶。
在全域範圍中 (沒有包在任何物件或函式內),this 通常指向全域物件
,例如 Window
(瀏覽器環境) 或 global
(Node.js 環境)。console.log(this); // this 會指向瀏覽器中的 Window 物件或 Node.js 的 global
this 通常指向調用這個方法的物件
。const obj = {
name: "威爾豬",
greet() {
console.log(`哈囉,${this.name}!`); // this 會指向 obj 物件
},
};
obj.greet(); // 輸出: 哈囉,威爾豬!
箭頭函式沒有自己的 this
,不會改變 this 的指向。const obj = {
name: "威爾豬",
greet: () => {
console.log(`哈囉,${this.name}!`); // this 會指向瀏覽器中的 Window 物件
},
};
obj.greet(); // 輸出: 哈囉, !
TypeScript 也會提醒我們 this 指向全域。
this 通常指向類別的實體
,可以在類別的方法中使用。class MyClass {
constructor(public name: string) {
this.name = name;
}
greet() {
console.log(`哈囉,${this.name}!`); // this 指向類別 MyClass
}
}
const myClass = new MyClass("威爾豬");
myClass.greet(); // 輸出: 哈囉,威爾豬!
call()
或 apply()
來改變指定函式的 this。兩者的差異為 call 傳入的參數用逗號隔開,而 apply 的參數則是使用陣列的形式。interface IPerson {
name: string;
}
function greet(this: IPerson) {
console.log(`哈囉,${this.name}!`); // 輸出: 哈囉,威爾豬!
}
const person: IPerson = { name: "威爾豬" };
greet.call(person); // this 會改變指向 person 物件
注意:
使用 call 或 apply 方法請使用傳統函式
,不要用箭頭函式,以避免運行出現非預期的效果。
函式可以接受一個或多個參數,在 TypeScript 中,函式宣告中使用的參數名稱,必須事先定義型別,以便在函式內部使用。
const add = (a: number, b: number): number => a + b;
console.log(add(2, 3)); // 輸出: 5
在 JavaScript 我們可以為函式的參數提供預設值,這樣在呼叫函式時如果未提供相應的參數,則會使用預設值,而在 TypeScript 的寫法為:
const greet = (name: string = "陌生人"): void => {
console.log(`哈囉,${name}!`);
};
greet(); // 輸出: 哈囉,陌生人!
greet("威爾豬"); // 輸出: 哈囉,威爾豬!
函式重載是指 定義多個具有相同名稱但具有不同參數類型或不同返回類型的函式
,在處理不同類型的輸入、不同參數個數或型別組合時非常有用,我們可以為同一個函式定義多種不同的參數組合和返回值型別。
function userInfo(name: string): void;
function userInfo(name: string, age: number): void;
function userInfo(name: string, age?: number): void {
if (age !== undefined) {
console.log(`姓名: ${name},年齡: ${age}`);
} else {
console.log(`姓名: ${name}`);
}
}
userInfo("威爾豬"); // 輸出: 姓名: 威爾豬
userInfo("威爾豬", 3); // 輸出: 姓名: 威爾豬,年齡: 3
在這個例子中,我們定義了一個 userInfo 函式的重載。第一個重載接受一個 name 參數,第二個重載接受 name 和 age 兩個參數。實際的函式實現在最後一個定義中,根據參數的數量進行不同的處理。
再來看另外一個例子:假設我們可能需要一個函式來計算圖形面積,並且根據形狀的不同提供不一樣的計算方式:
function handleArea(shape: "circle", radius: number): number;
function handleArea(shape: "rect", width: number, height: number): number;
function handleArea(shape: string, ...args: number[]): number {
if (shape === "circle") {
const [radius] = args;
return Math.round(Math.PI * radius ** 2 * 100) / 100;
} else if (shape === "rect") {
const [width, height] = args;
return width * height;
} else {
throw new Error("沒有這個圖形!");
}
}
console.log(handleArea("circle", 10)); // 輸出: 314.16
console.log(handleArea("rect", 4, 6)); // 輸出: 24
在這個例子中,我們定義了一個 handleArea 函式,一個用於計算圓形的面積,另一個用於計算矩形的面積。實際的函式實現使用 ...args
的數字陣列來接受可變數量的參數,再根據 shape 參數的值進行相應的計算。
當然我們也可以使用其它方式來代替函式重載,範例如下:
// 使用函式重載
function greet(name: string): string;
function greet(age: number): number;
function greet(nameOrAge: string | number): string | number {
if (typeof nameOrAge === "string") {
return `哈囉,${nameOrAge}!`;
} else {
return nameOrAge * 2;
}
}
console.log(greet("威爾豬")); // 輸出: 哈囉,威爾豬!
console.log(greet(3)); // 輸出: 6。
// 使用類型別名
type TGreet<T> = (nameOrAge: T) => T;
const greet: TGreet<string | number> = (nameOrAge) =>
typeof nameOrAge === "string" ? `哈囉,${nameOrAge}!` : nameOrAge * 2;
console.log(greet("威爾豬")); // 輸出: 哈囉,威爾豬!
console.log(greet(3)); // 輸出: 6
函式重載只是 TypeScript 中多態性的一種形式,還有其它方式可以實現類似的效果,簡單來說,合理運用函式和型別,以及注意 this 的指向,我們可以撰寫出更具靈活性和可維護性的程式碼。